package com.catena.core.chain;

import com.catena.core.CatenaOperationConstant;
import com.catena.core.NodeParameter;
import com.catena.core.ReturnDataIOException;
import com.catena.core.node.CatenaNode;
import com.catena.util.JsonUtil;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Objects;
import java.util.concurrent.BlockingQueue;

/**
 * Created by hx-pc on 16-2-25.
 */
public final class NodeOperationReturn extends CatenaOperationNodeAppender {

    private static final String START_TYPE = CatenaOperationConstant.START_TYPE;
    private static final String TARGET = CatenaOperationConstant.TARGET;
    private static final String RETURN_DATA_TYPE = CatenaOperationConstant.RETURN_DATA_TYPE;

    protected void execute(NodeParameter parameter) {
        if (Objects.equals(parameter.getOtherParam().get(START_TYPE), CatenaOperationConstant.START_TYPE_VALUE_RETURN)) {
            executeReturn(parameter);
        } else if (Objects.equals(parameter.getOtherParam().get(START_TYPE), CatenaOperationConstant.START_TYPE_VALUE_FORWARD)) {
            executeForward(parameter);
        } else if (Objects.equals(parameter.getOtherParam().get(START_TYPE), (CatenaOperationConstant.START_TYPE_VALUE_REDIRECT))) {
            executeRedirect(parameter);
        }
    }

    private void executeRedirect(NodeParameter parameter) {
        HttpServletResponse servletResponse = parameter.acceptServletResponse();
        try {
            servletResponse.sendRedirect(parameter.getOtherParam().get(TARGET).toString());
        } catch (IOException e) {
            throw new ReturnDataIOException(e);
        }
    }

    private void executeForward(NodeParameter parameter) {
        HttpServletRequest servletRequest = parameter.acceptServletRequest();
        HttpServletResponse servletResponse = parameter.acceptServletResponse();
        servletRequest.setAttribute(CatenaOperationConstant.START_RETURN_VALUE, parameter);
        try {
            servletRequest.getRequestDispatcher(parameter.getOtherParam().get(TARGET).toString()).forward(servletRequest, servletResponse);
        } catch (ServletException | IOException e) {
            throw new ReturnDataIOException(e);
        }
    }

    private void executeReturn(NodeParameter parameter) {
        HttpServletResponse servletResponse = parameter.acceptServletResponse();
        servletResponse.setHeader("Content-Type", "application/json");
        servletResponse.setStatus(parameter.getStatusCode());
        try {
            if (Objects.equals(parameter.getOtherParam().get(RETURN_DATA_TYPE), CatenaOperationConstant.RETURN_DATA_TYPE_STRING)) {
                servletResponse.getOutputStream().write(buildReturnObject(parameter).toString().getBytes());
            } else if (Objects.equals(parameter.getOtherParam().get(RETURN_DATA_TYPE), CatenaOperationConstant.RETURN_DATA_TYPE_OBJECT)) {
                servletResponse.getOutputStream().write(JsonUtil.writeValueAsString(buildReturnObject(parameter)).getBytes());
            }
        } catch (IOException e) {
            throw new ReturnDataIOException(e);
        }
    }

    private Object buildReturnObject(NodeParameter parameter) {
        if (Objects.nonNull(parameter.getOtherParam().get(CatenaOperationConstant.START_RETURN))) {
            return parameter.getData();
        } else {
            return parameter;
        }
    }

    @Override
    public void run(NodeParameter parameter, BlockingQueue<Method> methodQueue, BlockingQueue<CatenaNode> nodeQueue) {
        setMethod(methodQueue.peek());
        setExecuteNode(nodeQueue.peek());
        this.execute(parameter);
    }

}
